home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / dejagnu.lha / dejagnu-1.0.1 / expect / exp_inter.c < prev    next >
C/C++ Source or Header  |  1993-04-26  |  30KB  |  1,156 lines

  1. /* interact (using select) - give user keyboard control
  2.  
  3. Written by: Don Libes, NIST, 2/6/90
  4.  
  5. Design and implementation of this program was paid for by U.S. tax
  6. dollars.  Therefore it is public domain.  However, the author and NIST
  7. would appreciate credit if this program or parts of it are used.
  8.  
  9. $Revision: 1.7 $
  10. $Date: 1993/04/26 22:54:45 $
  11.  
  12. */
  13.  
  14. #include "exp_conf.h"
  15. #include <stdio.h>
  16. #include <sys/types.h>
  17. #include <sys/time.h>
  18. #include <ctype.h>
  19.  
  20. #include "tcl.h"
  21. #include "string.h"
  22.  
  23. #include "exp_tty.h"
  24. #include "exp_rename.h"
  25. #include "exp_global.h"
  26. #include "exp_command.h"
  27. #include "exp_log.h"
  28.  
  29. #include "regexp.h"
  30. #include "exp_regexp.h"
  31.  
  32. extern int errno;
  33.  
  34. #define new(x)    (x *)malloc(sizeof(x))
  35.  
  36. struct action {
  37.     char *statement;
  38.     int fast;        /* if true, skip tty mode changing */
  39.     int update;        /* if true, check implicit spawn_id */
  40.     struct action *next;    /* chain only for later for freeing */
  41. };
  42.  
  43. struct keymap {
  44.     char *keys;    /* original pattern provided by user */
  45.     regexp *re;
  46.     int case_sensitive;
  47.     int echo;    /* if keystrokes should be echoed */
  48.     int writethru;    /* if keystrokes should go through to process */
  49.     struct action action;
  50.     struct keymap *next;
  51. };
  52.  
  53. struct output {
  54.     int spawn_id;
  55.     struct f *f;
  56.     struct action *action_eof;
  57.     struct output *next;
  58. };
  59.  
  60. struct input {
  61.     int spawn_id;
  62.     struct f *f;
  63.     struct output *output;
  64.     struct action *action_eof;
  65.     struct action *action_timeout;
  66.     struct keymap *keymap;
  67.     int timeout_nominal;        /* timeout nominal */
  68.     int timeout_remaining;        /* timeout remaining */
  69.     struct input *next;
  70. };
  71.  
  72. /* record any use of implicit spawn_id here in the event that it */
  73. /* needs updating */
  74. struct update {
  75.     int *spawn_id;
  76.     struct update *next;
  77. };
  78.  
  79. static void free_input();
  80. static void free_keymap();
  81. static void free_output();
  82. static void free_action();
  83. static void free_update();
  84. static struct action *new_action();
  85. static struct output *new_output();
  86. static struct input *new_input();
  87. static struct update *new_update();
  88. static int inter_eval();
  89.  
  90. /* special pattern that signifies the expect interpreter itself */
  91. #define INTERPRETER_ACTION    "interpreter"
  92.  
  93. char update_error_string[] = "malloc failed: -update";
  94.  
  95. /* NB: FOLLOWING DESCRIPTION HAS NOT BEEN REVISED SINCE ADDITION OF REGEXP */
  96. /* This function accepts user keystrokes and returns one of the above values */
  97. /* describing whether the keystrokes match a key sequence, and could or */
  98. /* can't if more characters arrive */
  99. /* The function assigns a matching keymap if there is a match or can-match */
  100.  
  101. /* The basic idea of how this works is it does a smart sequential search. */
  102. /* It is optimized (if you can call it that) towards a small number of */
  103. /* key mappings, but still works well for large maps, since no function */
  104. /* calls are made, and we stop as soon as there is a single-char mismatch, */
  105. /* and go on to the next one.  A hash table or compiled DFA probably would */
  106. /* not buy very much here for most maps. */
  107.  
  108. static
  109. int
  110. in_keymap(string,stringlen,keymap,km_match,match_length,skip)
  111. char *string;
  112. int stringlen;
  113. struct keymap *keymap;        /* linked list of keymaps */
  114. struct keymap **km_match;    /* keymap that matches or can match */
  115. int *match_length;        /* # of chars that matched */
  116. int *skip;            /* # of chars to skip */
  117. {
  118.     struct keymap *km;
  119.     char *ks;        /* string from a keymap */
  120.     char *start_search;    /* where in the string to start searching */
  121.     char *string_end;
  122.  
  123.     /* assert (*km == 0) */
  124.  
  125.     /* a shortcut that should help master output which typically */
  126.     /* is lengthy and has no key maps.  Otherwise it would mindlessly */
  127.     /* iterate on each character anyway. */
  128.     if (!keymap) {
  129.         *skip = stringlen;
  130.         return(EXP_CANTMATCH);
  131.     }
  132.  
  133.     string_end = string + stringlen;
  134.  
  135.     /* Mark beginning of line for ^ . */
  136.     regbol = string;
  137.  
  138.     for (start_search = string;*start_search;start_search++) {
  139.     if (*km_match) break; /* if we've already found a CAN_MATCH */
  140.             /* don't bother starting search from positions */
  141.             /* further along the string */
  142.  
  143.     for (km=keymap;km;km=km->next) {
  144.         char *s;    /* current character being examined */
  145.  
  146.         if (!km->re) {
  147.         /* fixed string */
  148.         for (s = start_search,ks = km->keys ;;s++,ks++) {
  149.             /* if we hit the end of this map, must've matched! */
  150.             if (*ks == 0) {
  151.                 *skip = start_search-string;
  152.                 *match_length = s-start_search;
  153.                 *km_match = km;
  154.                 return(EXP_MATCH);
  155.             }
  156.  
  157.             /* if we ran out of user-supplied characters, and */
  158.             /* still haven't matched, it might match if the user */
  159.             /* supplies more characters next time */
  160.             if (s == string_end) {
  161.                 /* skip to next key entry, but remember */
  162.                 /* possibility that this entry might match */
  163.                 if (!*km_match) *km_match = km;
  164.                 break;
  165.             }
  166.             /* skip to next key entry, if characters don't match */
  167.             if ((*s & 0x7f) != *ks) break;
  168.         }
  169.         } else {
  170.         /* regexp */
  171.         int r;    /* regtry status */
  172.         regexp *prog = km->re;
  173.  
  174.         /* if anchored, but we're not at beginning, skip pattern */
  175.         if (prog->reganch) {
  176.             if (string != start_search) continue;
  177.         }
  178.  
  179.         /* known starting char - quick test 'fore lotta work */
  180.         if (prog->regstart) {
  181.             if ((*start_search & 0x7f) != prog->regstart) break;
  182.         }
  183.         r = regtry(prog,start_search,match_length);
  184.         if (r == EXP_MATCH) {
  185.             *km_match = km;
  186.             *skip = start_search-string;
  187.             return(EXP_MATCH);
  188.         }
  189.         if (r == EXP_CANMATCH) {
  190.             if (!*km_match) *km_match = km;
  191.         }
  192.         }
  193.     }
  194.     }
  195.  
  196.     if (*km_match) {
  197.         /* report a can-match */
  198.         *skip = (start_search-string)-1;
  199.         *match_length = stringlen - *skip;
  200.         return(EXP_CANMATCH);
  201.     }
  202.  
  203.     *skip = start_search-string;
  204.     return(EXP_CANTMATCH);
  205. }
  206.             
  207. #define finish(x)    { status = x; goto done; }
  208.  
  209. /*ARGSUSED*/
  210. int
  211. cmdInteract(clientData, interp, argc, argv)
  212. ClientData clientData;
  213. Tcl_Interp *interp;
  214. int argc;
  215. char **argv;
  216. {
  217.     /*declarations*/
  218.     int input_count;    /* count of struct input descriptors */
  219.     extern int fd_max;
  220.     struct input **fd_to_input;    /* map from fd's to "struct input"s */
  221.     int *fd_list;
  222.     struct keymap *km;    /* ptr for above while parsing */
  223.      extern char *tclRegexpError;    /* declared in tclInt.h */
  224.     extern int exp_dev_tty;
  225.     int master = EXP_SPAWN_ID_BAD;
  226.  
  227.     int all_fast = FALSE;    /* by default, turn off -f */
  228.     int next_fast = FALSE;    /* if we've seen a single -f */
  229.     int next_update = FALSE;/* if we've seen a single -update */
  230.     int next_re = FALSE;    /* if we've seen a single -re */
  231.     int next_writethru = FALSE;/*if macros should also go to proc output */
  232.     int next_echo = FALSE;    /* if macros should be echoed */
  233. /*    int next_case_sensitive = TRUE;*/
  234.     char **oldargv = 0;    /* save original argv here if we split it */
  235.     int status = TCL_OK;    /* final return value */
  236.     int i;            /* trusty temp */
  237.  
  238.     int timeout_simple = TRUE;    /* if no or global timeout */
  239.  
  240.     int tty_changed = FALSE;/* true if we had to change tty modes for */
  241.                 /* interact to work (i.e., to raw, noecho) */
  242.     int was_raw;
  243.     int was_echo;
  244.     exp_tty tty_old;
  245.  
  246.     int replace_user_by_process = EXP_SPAWN_ID_BAD; /* for -u flag */
  247.  
  248.     struct input *input_user;
  249.     struct input *input_default;
  250. #define input_base input_user
  251.     struct input *inp;    /* overused ptr to struct input */
  252.     struct output *outp;    /* overused ptr to struct output */
  253.  
  254.     int dash_input_count = 0; /* # of "-input"s seen */
  255.     int dash_output_count = 0; /* # of "-output"s seen */
  256.     int arbitrary_timeout;
  257.     int default_timeout;
  258.     struct action action_timeout;    /* common to all */
  259.     struct action action_eof;    /* common to all */
  260.     struct action **action_eof_ptr;    /* allow -input/ouput to */
  261.         /* leave their eof-action assignable by a later */
  262.         /* -eof */
  263.     struct action *action_base = 0;
  264.     struct update *update_base = 0;
  265.  
  266.     struct keymap **end_km;
  267.  
  268.     int key;
  269.  
  270.     if ((argc == 2) && exp_one_arg_braced(argv[1])) {
  271.         return(exp_eval_with_one_arg(clientData,interp,argc,argv));
  272.     }
  273.  
  274.     argv++;
  275.     argc--;
  276.  
  277.     default_timeout = EXP_TIME_INFINITY;
  278.     arbitrary_timeout = EXP_TIME_INFINITY;    /* if user specifies */
  279.         /* a bunch of timeouts with EXP_TIME_INFINITY, this will be */
  280.         /* left around for us to find. */
  281.  
  282.     if (!(input_user = new_input(interp))) return(TCL_ERROR);
  283.     input_user->spawn_id = 0;    /* stdin by default */
  284.     input_user->output = 0;
  285.     input_user->action_eof = &action_eof;
  286.     input_user->timeout_nominal = EXP_TIME_INFINITY;
  287.     input_user->action_timeout = 0;
  288.     input_user->keymap = 0;
  289.     end_km = &input_user->keymap;
  290.     inp = input_user;
  291.     action_eof_ptr = &input_user->action_eof;
  292.  
  293.     if (!(input_default = new_input(interp))) return(TCL_ERROR);
  294.     input_default->spawn_id = EXP_SPAWN_ID_BAD;    /* fix later */
  295.     input_default->output = 0;
  296.     input_default->action_eof = &action_eof;
  297.     input_default->timeout_nominal = EXP_TIME_INFINITY;
  298.     input_default->action_timeout = 0;
  299.     input_default->keymap = 0;
  300.     input_default->next = 0;        /* no one else */
  301.     input_user->next = input_default;
  302.  
  303.     input_count = 2;
  304.  
  305.     action_eof.statement = "return";
  306.     action_eof.fast = TRUE;
  307.     action_timeout.fast = TRUE;
  308.  
  309.     for (;argc>0;argc--,argv++) {
  310.         if (streq(*argv,"-input")) {
  311.             dash_input_count++;
  312.             if (dash_input_count == 2) {
  313.                 inp = input_default;
  314.             } else if (dash_input_count > 2) {
  315.                 struct input *previous_input = inp;
  316.                 if (!(inp = new_input(interp)))
  317.                     return(TCL_ERROR);
  318.                 previous_input->next = inp;
  319.                 input_count++;
  320.             }
  321.             inp->output = 0;
  322.             inp->action_eof = &action_eof;
  323.             action_eof_ptr = &inp->action_eof;
  324.             inp->timeout_nominal = default_timeout;
  325.             inp->action_timeout = &action_timeout;
  326.             inp->keymap = 0;
  327.             end_km = &inp->keymap;
  328.             inp->next = 0;
  329.             argc--;argv++;
  330.             if (argc < 1) {
  331.                 exp_error(interp,"-input needs argument");
  332.                 return(TCL_ERROR);
  333.             }
  334.             inp->spawn_id = atoi(*argv);
  335.         } else if (streq(*argv,"-output")) {
  336.             struct output *tmp;
  337.  
  338.             dash_output_count++;
  339.  
  340.             /* imply a "-input" */
  341.             if (dash_input_count == 0) dash_input_count = 1;
  342.  
  343.             if (!(outp = new_output(interp))) return(TCL_ERROR);
  344.  
  345.             /* link new output in front of others */
  346.             tmp = inp->output;
  347.             inp->output = outp;
  348.             outp->next = tmp;
  349.  
  350.             argc--;argv++;
  351.             if (argc < 1) {
  352.                 exp_error(interp,"-output needs argument");
  353.                 return(TCL_ERROR);
  354.             }
  355.             /* accept Tcl file handles, too */
  356.             /* the following expression looks stupid but we */
  357.             /* really have to verify that at least four chars */
  358.             /* exist before we bop on over to the 5th one so */
  359.             /* we might as well verify them while we're at it */
  360.             if ((argv[0][0] == 'f') && (argv[0][1] == 'i')
  361.                 && (argv[0][2] == 'l') && (argv[0][3] == 'e')) {
  362.                 outp->spawn_id = atoi(*argv + 4);
  363.             } else     outp->spawn_id = atoi(*argv);
  364.             outp->action_eof = &action_eof;
  365.             action_eof_ptr = &outp->action_eof;
  366.         } else if (streq(*argv,"-u")) {    /* treat process as user */
  367.             argc--;argv++;
  368.             if (argc < 1) {
  369.                 exp_error(interp,"-u needs argument");
  370.                 return(TCL_ERROR);
  371.             }
  372.             replace_user_by_process = atoi(*argv);
  373.  
  374.             /* imply a "-input" */
  375.             if (dash_input_count == 0) dash_input_count = 1;
  376.         } else if (streq(*argv,"-o")) {    /* apply following patterns */
  377.                         /* to opposite side of */
  378.                         /* interaction */
  379.             end_km = &input_default->keymap;
  380.  
  381.             /* imply two "-input" */
  382.             if (dash_input_count < 2) {
  383.                 dash_input_count = 2;
  384.                 inp = input_default;
  385.                 action_eof_ptr = &inp->action_eof;
  386.             }
  387.         } else if (streq(*argv,"-i")) { /* substitute master */
  388.             argc--;argv++;
  389.             master = atoi(*argv);
  390.             /* will be used later on */
  391.  
  392.             /* imply two "-input" */
  393.             if (dash_input_count < 2) {
  394.                 dash_input_count = 2;
  395.                 inp = input_default;
  396.                 action_eof_ptr = &inp->action_eof;
  397.             }
  398. /*        } else if (streq(*argv,"-nocase")) {*/
  399. /*            next_case_sensitive = FALSE;*/
  400.         } else if (streq(*argv,"-re")) {
  401.             if (argc < 1) {
  402.                 exp_error(interp,"-re needs pattern");
  403.                 return(TCL_ERROR);
  404.             }
  405.             next_re = TRUE;
  406.         } else if (streq(*argv,"-echo")) {
  407.             next_echo = TRUE;
  408.         } else if (streq(*argv,"-flush")) {
  409.             next_writethru = TRUE;
  410.         } else if (streq(*argv,"-f")) {
  411.             if (argc < 1) {
  412.                 exp_error(interp,"-f needs pattern");
  413.                 return(TCL_ERROR);
  414.             }
  415.             next_fast = TRUE;
  416.         } else if (streq(*argv,"-F")) {
  417.             all_fast = TRUE;
  418.         } else if (streq(*argv,"-update")) {
  419.             next_update = TRUE;
  420.         } else if (streq(*argv,"-eof")) {
  421.             struct action *action;
  422.  
  423.             argc--;argv++;
  424.  
  425.             /* if -eof comes before "-input", then applies */
  426.             /* to all descriptors, else just the current one */
  427.             if (dash_input_count > 0 || dash_output_count > 0) {
  428.                 *action_eof_ptr = action =
  429.                     new_action(&action_base);
  430.                 if (!action) {
  431.                     exp_error(interp,"malloc failed: -action");
  432.                     return(TCL_ERROR);
  433.                 }
  434.             } else {
  435.                 action = &action_eof;
  436.             }
  437.             action->statement = *argv;
  438.             argc--;argv++;
  439.             if (all_fast || next_fast) {
  440.                 action->fast = TRUE;
  441.                 next_fast = FALSE;
  442.             }
  443.             if (next_update) {
  444.                 action->update = TRUE;
  445.                 next_update = FALSE;
  446.             }
  447.         } else if (streq(*argv,"-timeout")) {
  448.             int t;
  449.             struct action *action;
  450.  
  451.             argc--;argv++;
  452.             if (argc < 1) {
  453.                 exp_error(interp,"-timeout needs time");
  454.                 return(TCL_ERROR);
  455.             }
  456.  
  457.             t = atoi(*argv);
  458.             argc--;argv++;
  459.             if (t != -1)
  460.                 arbitrary_timeout = t;
  461.             /* we need an arbitrary timeout to start */
  462.             /* search for lowest one later */
  463.  
  464.  
  465.             /* if -timeout comes before "-input", then applies */
  466.             /* to all descriptors, else just the current one */
  467.             if (dash_input_count > 0) {
  468.                 timeout_simple = FALSE;
  469.                 action = inp->action_timeout;
  470.                 inp->timeout_nominal = t;
  471.             } else {
  472.                 action = &action_timeout;
  473.                 default_timeout = t;
  474.             }
  475.             action->statement = *argv;
  476.             argc--;argv++;
  477.             action->fast = (all_fast || next_fast);
  478.             next_fast = FALSE;
  479.             action->update = next_update;
  480.             next_update = FALSE;
  481.         } else if (streq(*argv,"-brace")) {
  482.             /* allow exp_eval_with_one_arg to work */
  483.         } else {
  484.             km = new(struct keymap);
  485.             if (!km) {
  486.                 exp_error(interp,"malloc failed (struct keymap)");
  487.                 return(TCL_ERROR);
  488.             }
  489.  
  490.             /* so that we can match in order user specified */
  491.             /* link to end of keymap list */
  492.             *end_km = km;
  493.             km->next = 0;
  494.             end_km = &km->next;
  495.  
  496.             km->echo = next_echo;
  497.             km->writethru = next_writethru;
  498.             km->action.fast = (all_fast || next_fast);
  499.             km->action.update = next_update;
  500. /*            km->case_sensitive = next_case_sensitive;*/
  501.  
  502.             next_echo = next_writethru = next_fast = FALSE;
  503.             next_update = FALSE;
  504. /*            next_case_sensitive = TRUE;*/
  505.  
  506.             km->keys = *argv;
  507.  
  508.             if (next_re) {
  509.                 if (0 == (km->re = regcomp(*argv))) {
  510.                     exp_error(interp,"bad regular expression: %s",
  511.                         tclRegexpError);
  512.                     return(TCL_ERROR);
  513.                 }
  514.             } else {
  515.                 km->re = 0;
  516.                 /* should really compare to f->umsize, but */
  517.                 /* it's hard to get at this point */
  518.                 if (BUFSIZ < strlen(km->keys)) {
  519.                     exp_error(interp,"key sequence \"%s\"> match_max (%s bytes)",km->keys,BUFSIZ);
  520.                     return(TCL_ERROR);
  521.                 }
  522.             }
  523.             argc--;argv++;
  524.  
  525.             km->action.statement = *argv;
  526.             debuglog("defining key %s, action %s\r\n",
  527.              km->keys,
  528.              km->action.statement?(dprintify(km->action.statement))
  529.                    :INTERPRETER_ACTION);
  530.  
  531.             /* imply a "-input" */
  532.             if (dash_input_count == 0) dash_input_count = 1;
  533.         }
  534.     }
  535.  
  536.     /* if the user has not supplied either "-output" for the */
  537.     /* default two "-input"s, fix them up here */
  538.  
  539.     if (!input_user->output) {
  540.         int set_from_master = FALSE;
  541.         struct update *u;
  542.  
  543.         struct output *o = new_output(interp);
  544.  
  545.         if (!o) return(TCL_ERROR);
  546.         if (master == EXP_SPAWN_ID_BAD) {
  547.             if (0 == exp_update_master(interp,&master,1,1)) {
  548.                 return(TCL_ERROR);
  549.             }
  550.             set_from_master = TRUE;
  551.         }
  552.          o->spawn_id = master;
  553.         o->next = 0;    /* no one else */
  554.         o->action_eof = &action_eof;
  555.         input_user->output = o;
  556.  
  557.         if (set_from_master) {
  558.             u = new_update(&update_base,&input_user->output->spawn_id);
  559.             if (!u) {
  560.                 exp_error(interp,update_error_string);
  561.                 return(TCL_ERROR);
  562.             }
  563.         }
  564.     }
  565.  
  566.     if (!input_default->output) {
  567.         struct output *o = new_output(interp);
  568.  
  569.         if (!o) return(TCL_ERROR);
  570.          o->spawn_id = 1;    /* stdout by default */
  571.         o->next = 0;    /* no one else */
  572.         o->action_eof = &action_eof;
  573.         input_default->output = o;
  574.     }
  575.  
  576.     /* if user has given "-u" flag, substitute process for user */
  577.     /* in first two -inputs */
  578.     if (replace_user_by_process != EXP_SPAWN_ID_BAD) {
  579.         input_user->spawn_id =           replace_user_by_process;
  580.         input_default->output->spawn_id = replace_user_by_process;
  581.     }
  582.  
  583.     if (input_default->spawn_id == EXP_SPAWN_ID_BAD) {
  584.         struct update *u;
  585.  
  586.         if (master == EXP_SPAWN_ID_BAD) {
  587.             if (0 == exp_update_master(interp,&master,1,1)) {
  588.                 return(TCL_ERROR);
  589.             }
  590.             u = new_update(&update_base,&input_default->spawn_id);
  591.             if (!u) {
  592.                 exp_error(interp,update_error_string);
  593.                 return(TCL_ERROR);
  594.             }
  595.         }
  596.         input_default->spawn_id = master;
  597.     }
  598.  
  599.     if (input_user->spawn_id == input_default->spawn_id) {
  600.         exp_error(interp,"cannot interact with self - set spawn_id to a spawned process");
  601.         return(TCL_ERROR);
  602.     }
  603.  
  604.  
  605. /* if we are running this using /dev/tty */
  606. #define realtty(x)    ((x == 1) || (x == exp_dev_tty && exp_dev_tty != -1))
  607. #define REALTTY ((input_user->spawn_id == 0) || \
  608.          (input_user->spawn_id == exp_dev_tty && exp_dev_tty != -1))
  609.  
  610.     if (REALTTY) {
  611.         tty_changed = tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
  612.     }
  613.  
  614.     /* build args for ready() */
  615.     fd_list = (int *)malloc(input_count * sizeof(int));
  616.  
  617.     fd_to_input = (struct input **)malloc((fd_max+1) * sizeof(struct input *));
  618.     /* at this point, we can now "goto done"; doing so earlier will */
  619.     /* screw up memory allocator */
  620.  
  621.     for (inp = input_base,i=0;inp;inp=inp->next,i++) {
  622.         /* validate all the input descriptors */
  623.         if (!(inp->f = exp_fd2f(interp,inp->spawn_id,1,1,"interact")))
  624.             finish(TCL_ERROR);
  625.  
  626.         /* build map to translate from spawn_id to struct input */
  627.         fd_to_input[inp->spawn_id] = inp;
  628.  
  629.         /* build input to ready() */
  630.         fd_list[i] = inp->spawn_id;
  631.  
  632.         /* start timers */
  633.         inp->timeout_remaining = inp->timeout_nominal;
  634.     }
  635.  
  636.     key = expect_key++;
  637.  
  638. #ifndef SIMPLE_EVENT
  639.  
  640.     /* loop waiting (in event handler) for input */
  641.     for (;;) {
  642.         int te;    /* result of Tcl_Eval */
  643.         struct f *u;
  644.         int rc;    /* return code from ready.  This is further */
  645.             /* refined by matcher. */
  646.         int cc;    /* chars count from read() */
  647.         int m;    /* master */
  648.         struct action *action = 0;
  649.         time_t previous_time;
  650.         time_t current_time;
  651.         int match_length, skip;
  652.         int change;    /* if action requires cooked mode */
  653.         int attempt_match = TRUE;
  654.         struct input *soonest_input;
  655.         int print;        /* # of chars to print */
  656.         int oldprinted;        /* old version of u->printed */
  657.  
  658.         int timeout;    /* current as opposed to default_timeout */
  659.  
  660.         /* calculate how long to wait */
  661.         /* by finding shortest remaining timeout */
  662.         if (timeout_simple) {
  663.             timeout = default_timeout;
  664.         } else {
  665.             timeout = arbitrary_timeout;
  666.  
  667.             for (inp=input_base;inp;inp=inp->next) {
  668.                 if ((inp->timeout_remaining != EXP_TIME_INFINITY) &&
  669.                     (inp->timeout_remaining < timeout))
  670.                     soonest_input = inp;
  671.                     timeout = inp->timeout_remaining;
  672.             }
  673.  
  674.             time(&previous_time);
  675.             /* timestamp here rather than simply saving old */
  676.             /* current time (after ready()) to account for */
  677.             /* possibility of slow actions */
  678.  
  679.             /* timeout can actually be EXP_TIME_INFINITY here if user */
  680.             /* explicitly supplied it in a few cases (or */
  681.             /* the count-down code is broken) */
  682.         }
  683.  
  684.         rc = exp_get_next_event(interp,fd_list,input_count,&m,timeout,key);
  685.         if (rc == EXP_TCLERROR)
  686.             return(TCL_ERROR);
  687.  
  688.         if (rc == EXP_TIMEOUT) {
  689.             if (timeout_simple) {
  690.                 action = &action_timeout;
  691.                 goto got_action;
  692.             } else {
  693.                 action = soonest_input->action_timeout;
  694.             }
  695.         }
  696.         if (!timeout_simple) {
  697.             int time_diff;
  698.  
  699.             time(¤t_time);
  700.             time_diff = current_time - previous_time;
  701.  
  702.             /* update all timers */
  703.             for (inp=input_base;inp;inp=inp->next) {
  704.                 if (inp->timeout_remaining != EXP_TIME_INFINITY) {
  705.                     inp->timeout_remaining -= time_diff;
  706.                     if (inp->timeout_remaining < 0)
  707.                         inp->timeout_remaining = 0;
  708.                 }
  709.             }
  710.         }
  711.  
  712.         /* at this point, we have some kind of event which can be */
  713.         /* immediately processed - i.e. something that doesn't block */
  714.  
  715.         /* figure out who we are */
  716.         inp = fd_to_input[m];
  717.         u = inp->f;
  718.  
  719.         /* reset timer */
  720.         inp->timeout_remaining = inp->timeout_nominal;
  721.  
  722.         switch (rc) {
  723.         case EXP_DATA_NEW:
  724.             cc = read(m,    u->buffer + u->size,
  725.                     u->msize - u->size);
  726.             if (cc > 0) {
  727.                 fs[m].key = key;
  728.                 u->size += cc;
  729.                 u->buffer[u->size] = '\0';
  730.  
  731.                 /* strip parity if requested */
  732.                 if (u->parity == 0) {
  733.                     /* do it from end backwards */
  734.                     char *p = u->buffer + u->size - 1;
  735.                     int count = cc;
  736.                     while (count--) {
  737.                         *p-- &= 0x7f;
  738.                     }
  739.                 }
  740.  
  741.                 /* avoid another function call if possible */
  742.                 if (debugfile || is_debugging) {
  743.                     debuglog("spawn id %d sent {%s}\r\n",m,
  744.                         printify(u->buffer + u->size - cc));
  745.                 }
  746.                 break;
  747.             }
  748.             /*FALLTHRU*/
  749.  
  750.             /* Most systems have read() return 0, allowing */
  751.             /* control to fall thru and into this code.  On some */
  752.             /* systems (currently HP and new SGI), read() does */
  753.             /* see eof, and it must be detected earlier.  Then */
  754.             /* control jumps directly to this EXP_EOF label. */
  755.         case EXP_EOF:
  756.             action = inp->action_eof;
  757.             attempt_match = FALSE;
  758.             skip = u->size;
  759.             rc = EXP_EOF;
  760.             debuglog("interact: received eof from spawn_id %d\r\n",m);
  761.             exp_close(interp,m);
  762.             break;
  763.         case EXP_DATA_OLD:
  764.             cc = 0;
  765.             break;
  766.         case EXP_TIMEOUT:
  767.             action = inp->action_timeout;
  768.             attempt_match = FALSE;
  769.             skip = u->size;
  770.             break;
  771.         }
  772.  
  773.         km = 0;
  774.  
  775.         if (attempt_match) {
  776.             rc = in_keymap(u->buffer,u->size,inp->keymap,
  777.                 &km,&match_length,&skip);
  778.         } else {
  779.             attempt_match = TRUE;
  780.         }
  781.  
  782.         /* put regexp result in variables */
  783.         if (km && km->re) {
  784. #define INTER_OUT "interact_out"
  785. #define out(i,val)  debuglog("expect: set %s(%s) {%s}\r\n",INTER_OUT,i, \
  786.                         dprintify(val)); \
  787.             if (!Tcl_SetVar2(interp,INTER_OUT,i,val,0)) \
  788.                 {status = TCL_ERROR; goto done;}
  789.  
  790.             char name[20], value[20];
  791.             regexp *re = km->re;
  792.             char match_char;/* place to hold char temporarily */
  793.                     /* uprooted by a NULL */
  794.  
  795.             for (i=0;i<NSUBEXP;i++) {
  796.                 int offset;
  797.  
  798.                 if (re->startp[i] == 0) break;
  799.  
  800.                 /* start index */
  801.                 sprintf(name,"%d,start",i);
  802.                 offset = re->startp[i]-u->buffer;
  803.                 sprintf(value,"%d",offset);
  804.                 out(name,value);
  805.  
  806.                 /* end index */
  807.                 sprintf(name,"%d,end",i);
  808.                 sprintf(value,"%d",re->endp[i]-u->buffer-1);
  809.                 out(name,value);
  810.  
  811.                 /* string itself */
  812.                 sprintf(name,"%d,string",i);
  813.                 /* temporarily null-terminate in */
  814.                 /* middle */
  815.                 match_char = *re->endp[i];
  816.                 *re->endp[i] = 0;
  817.                 out(name,re->startp[i]);
  818.                 *re->endp[i] = match_char;
  819.             }
  820.         }
  821.  
  822.         /* dispose of chars that should be skipped */
  823.         
  824.         /* skip is chars not involved in match */
  825.         /* print is with chars involved in match */
  826.  
  827.         if (km && km->writethru) {
  828.             print = skip + match_length;
  829.         } else print = skip;
  830.  
  831.         /* figure out if we should echo any chars */
  832.         if (km && km->echo) {
  833.             int seen;    /* either printed or echoed */
  834.  
  835.             /* echo to stdout rather than stdin */
  836.             if (m == 0) m = 1;
  837.  
  838.             /* write is unlikely to fail, since we just read */
  839.             /* from same descriptor */
  840.             seen = u->printed + u->echoed;
  841.             if (skip >= seen) {
  842.                 write(m,u->buffer+skip,match_length);
  843.             } else if ((match_length + skip - seen) > 0) {
  844.                 write(m,u->buffer+seen,match_length+skip-seen);
  845.             }
  846.             u->echoed = match_length + skip - u->printed;
  847.         }
  848.  
  849.         oldprinted = u->printed;
  850.  
  851.         /* If expect has left characters in buffer, it has */
  852.         /* already echoed them to the screen, thus we must */
  853.         /* prevent them being rewritten.  Unfortunately this */
  854.         /* gives the possibility of matching chars that have */
  855.         /* already been output, but we do so since the user */
  856.         /* could have avoided it by flushing the output */
  857.         /* buffers directly. */
  858.         if (print > u->printed) {    /* usual case */
  859.             int wc;    /* return code from write() */
  860.             for (outp = inp->output;outp;outp=outp->next) {
  861.  
  862.                 /* send to logfile if open */
  863.                 /* and user is seeing it */
  864.                 if (logfile && realtty(outp->spawn_id)) {
  865.                     fwrite(u->buffer+u->printed,1,
  866.                            print - u->printed,logfile);
  867.                 }
  868.  
  869.                 /* send to each output descriptor */
  870.                 wc = write(outp->spawn_id,u->buffer+u->printed,
  871.                     print - u->printed);
  872.                 if (wc <= 0) {
  873.                     debuglog("interact: write on spawn id %d failed (errno = %d)\r\n",outp->spawn_id,errno);
  874.                     action = outp->action_eof;
  875.                     change = !(action && action->fast);
  876.  
  877.                     if (change && tty_changed)
  878.                         tty_set(interp,&tty_old,was_raw,was_echo);
  879.                     te = inter_eval(interp,action,fd_to_input,fd_list,
  880.                         update_base,input_base);
  881.  
  882.                     if (change && REALTTY) tty_changed =
  883.                        tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
  884.                     switch (te) {
  885.                     case TCL_BREAK:
  886.                     case TCL_CONTINUE:
  887.                         finish(te);
  888.                     case TCL_RETURN_TCL:
  889.                         finish(TCL_RETURN);
  890.                     case TCL_RETURN:
  891.                         finish(TCL_OK);
  892.                     case TCL_OK:
  893.                         /* god knows what the user might */
  894.                         /* have done to us in the way of */
  895.                         /* closed fds, so .... */
  896.                         action = 0;    /* reset action */
  897.                         continue;
  898.                     default:
  899.                         finish(TCL_ERROR);
  900.                     }
  901.                 }
  902.             }
  903.             u->printed = print;
  904.         }
  905.  
  906.         /* u->printed is now accurate with respect to the buffer */
  907.         /* However, we're about to shift the old data out of the */
  908.         /* buffer.  Thus, u->size, printed, and echoed must be */
  909.         /* updated */
  910.  
  911.         /* first update size based on skip information */
  912.         /* then set skip to the total amount skipped */
  913.  
  914.         if (rc == EXP_MATCH) {
  915.             action = &km->action;
  916.  
  917.             skip += match_length;
  918.             u->size -= skip;
  919.  
  920.             if (u->size)
  921.                 memcpy(u->buffer, u->buffer + skip, u->size);
  922.                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
  923.         } else {
  924.             if (skip) {
  925.                 u->size -= skip;
  926.                 memcpy(u->buffer, u->buffer + skip, u->size);
  927.                 exp_lowmemcpy(u->lower,u->buffer+ skip, u->size);
  928.             }
  929.         }
  930.  
  931.         /* as long as buffer is still around, null terminate it */
  932.         if (rc != EXP_EOF) {
  933.             u->buffer[u->size] = '\0';
  934.             u->lower [u->size] = '\0';
  935.         }
  936.         /* now update printed based on total amount skipped */
  937.  
  938.         u->printed -= skip;
  939.         /* if more skipped than printed (i.e., keymap encountered) */
  940.         /* for printed positive */
  941.         if (u->printed < 0) u->printed = 0;
  942.  
  943.         /* if we are in the middle of a match, force the next event */
  944.         /* to wait for more data to arrive */
  945.         u->force_read = (rc == EXP_CANMATCH);
  946.  
  947.         /* finally reset echoed if necessary */
  948.         if (rc != EXP_CANMATCH) {
  949.             if (skip >= oldprinted + u->echoed) u->echoed = 0;
  950.         }
  951.  
  952.         if (action) {
  953. got_action:
  954.             change = !(action && action->fast);
  955.  
  956.             if (change && tty_changed)
  957.                 tty_set(interp,&tty_old,was_raw,was_echo);
  958.  
  959.             te = inter_eval(interp,action,fd_to_input,fd_list,
  960.                         update_base,input_base);
  961.  
  962.             if (change && REALTTY) tty_changed =
  963.                tty_raw_noecho(interp,&tty_old,&was_raw,&was_echo);
  964.             switch (te) {
  965.             case TCL_BREAK:
  966.             case TCL_CONTINUE:
  967.                 finish(te);
  968.             case TCL_RETURN_TCL:
  969.                 finish(TCL_RETURN);
  970.             case TCL_RETURN:
  971.                 finish(TCL_OK);
  972.             case TCL_OK:
  973.                 /* god knows what the user might */
  974.                 /* have done to us in the way of */
  975.                 /* closed fds, so .... */
  976.                 action = 0;    /* reset action */
  977.                 continue;
  978.             default:
  979.                 finish(TCL_ERROR);
  980.             }
  981.         }
  982.     }
  983.  
  984. #else /* SIMPLE_EVENT */
  985.     exp_error(interp,"interact not yet supported on systems that lack poll/select");
  986.     status = TCL_ERROR;
  987. #endif /* SIMPLE_EVENT */
  988.  
  989.  done:
  990.     if (tty_changed) tty_set(interp,&tty_old,was_raw,was_echo);
  991.     if (oldargv) free((char *)argv);
  992.     free((char *)fd_list);
  993.     free((char *)fd_to_input);
  994.     free_input(input_base);
  995.     free_action(action_base);
  996.     free_update(update_base);
  997.  
  998.     return(status);
  999. }
  1000.  
  1001. /* version of Tcl_Eval for interact */ 
  1002. static int
  1003. inter_eval(interp,action,fd_to_input,fd_list,update,input_base)
  1004. Tcl_Interp *interp;
  1005. struct action *action;
  1006. struct input **fd_to_input;
  1007. int *fd_list;
  1008. struct update *update;
  1009. struct input *input_base;
  1010. {
  1011.     struct input *inp;
  1012.     int master;
  1013.     int te;
  1014.     int i;
  1015.  
  1016.     if (action->statement) {
  1017.         te = Tcl_Eval(interp,action->statement,0,(char **)0);
  1018.     } else {
  1019.         nflog("\r\n",1);
  1020.         te = exp_interpreter(interp);
  1021.     }
  1022.  
  1023.     /* don't validate if we're exiting anyway */
  1024.     if (te != TCL_OK) return(te);
  1025.  
  1026.     if (action->update) {
  1027.         if (0 == exp_update_master(interp,&master,1,0)) {
  1028.             return(TCL_ERROR);
  1029.         }
  1030.         for (;update;update = update->next) {
  1031.             *update->spawn_id = master;
  1032.         }
  1033.     }
  1034.     /* revalidate all input descriptors */
  1035.     for (inp = input_base,i=0;inp;inp=inp->next,i++) {
  1036.         if (!(inp->f = exp_fd2f(interp,inp->spawn_id,1,0,"interact")))
  1037.             return(TCL_ERROR);
  1038.  
  1039.         /* build map to translate from spawn_id to struct input */
  1040.         fd_to_input[inp->spawn_id] = inp;
  1041.  
  1042.         /* build input to ready() */
  1043.         fd_list[i] = inp->spawn_id;
  1044.     }
  1045.  
  1046.     return te;
  1047. }
  1048.  
  1049. static void
  1050. free_keymap(km)
  1051. struct keymap *km;
  1052. {
  1053.     if (km == 0) return;
  1054.     free_keymap(km->next);
  1055.  
  1056.     free((char *)km);
  1057. }
  1058.  
  1059. static void
  1060. free_action(a)
  1061. struct action *a;
  1062. {
  1063.     struct action *next;
  1064.  
  1065.     while (a) {
  1066.         next = a->next;
  1067.         free((char *)a);
  1068.         a = next;
  1069.     }
  1070. }
  1071.  
  1072. static void
  1073. free_input(i)
  1074. struct input *i;
  1075. {
  1076.     if (i == 0) return;
  1077.     free_input(i->next);
  1078.  
  1079.     free_output(i->output);
  1080.     free_keymap(i->keymap);
  1081.     free((char *)i);
  1082. }
  1083.  
  1084. static struct action *
  1085. new_action(base)
  1086. struct action **base;
  1087. {
  1088.     struct action *o = new(struct action);
  1089.     if (!o) return 0;
  1090.  
  1091.     /* stick new action into beginning of list of all actions */
  1092.     o->next = *base;
  1093.     *base = o;
  1094.  
  1095.     return o;
  1096. }
  1097.  
  1098. static struct update *
  1099. new_update(base,fd_ptr)
  1100. struct update **base;
  1101. int *fd_ptr;
  1102. {
  1103.     struct update *u = new(struct update);
  1104.     if (!u) return 0;        
  1105.  
  1106.     /* stick new update into beginning of list of all updates */
  1107.     u->next = *base;
  1108.     *base = u;
  1109.  
  1110.     u->spawn_id = fd_ptr;
  1111.  
  1112.     return u;
  1113. }
  1114.  
  1115. static void
  1116. free_update(u)
  1117. struct update *u;
  1118. {
  1119.     struct update *next;
  1120.  
  1121.     while (u) {
  1122.         next = u->next;
  1123.         free((char *)u);
  1124.         u = next;
  1125.     }
  1126. }
  1127.  
  1128. static struct output *
  1129. new_output(interp)
  1130. Tcl_Interp *interp;
  1131. {
  1132.     struct output *o = new(struct output);
  1133.  
  1134.     if (!o) exp_error(interp,"malloc failed: -output");
  1135.     return o;
  1136. }
  1137.  
  1138. static struct input *
  1139. new_input(interp)
  1140. Tcl_Interp *interp;
  1141. {
  1142.     struct input *i = new(struct input);
  1143.     if (!i) exp_error(interp,"malloc failed: -input");
  1144.     return i;
  1145. }
  1146.  
  1147. static void
  1148. free_output(o)
  1149. struct output *o;
  1150. {
  1151.     if (o == 0) return;
  1152.     free_output(o->next);
  1153.  
  1154.     free((char *)o);
  1155. }
  1156.